/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_
#define ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_

#include "ic_runtime_stub.h"
#include "ic_handler.h"
#include "ic_runtime.h"
#include "profile_type_info.h"
#include "plugins/ecmascript/runtime/js_tagged_value-inl.h"
#include "plugins/ecmascript/runtime/js_array.h"
#include "plugins/ecmascript/runtime/js_hclass-inl.h"
#include "plugins/ecmascript/runtime/global_dictionary-inl.h"
#include "plugins/ecmascript/runtime/js_function.h"
#include "plugins/ecmascript/runtime/js_proxy.h"
#include "plugins/ecmascript/runtime/global_env.h"
#include "plugins/ecmascript/runtime/object_factory-inl.h"
#include "plugins/ecmascript/runtime/js_handle.h"
#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h"
#include "plugins/ecmascript/runtime/ic/proto_change_details.h"

#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h"
#include "plugins/ecmascript/runtime/runtime_call_id.h"

namespace ark::ecmascript {
bool ICRuntimeStub::HaveICForFunction(JSFunction *func)
{
    return !func->GetProfileTypeInfo().IsUndefined();
}

uint32_t ICRuntimeStub::MapSlotId(JSFunction *func, uint32_t slotId)
{
    uint8_t *mapping = func->GetMethod()->GetICMapping();
    ASSERT(mapping != nullptr);
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    return mapping[slotId];
}

ProfileTypeInfo *ICRuntimeStub::GetRuntimeProfileTypeInfo(JSFunction *func)
{
    JSTaggedValue profileTypeInfo = func->GetProfileTypeInfo();
    return ProfileTypeInfo::Cast(profileTypeInfo.GetTaggedObject());
}

JSTaggedValue ICRuntimeStub::LoadGlobalICByName(JSThread *thread, JSFunction *func, JSTaggedValue globalValue,
                                                JSTaggedValue key, uint32_t slotId, bool isTryLoad)
{
    INTERPRETER_TRACE(thread, LoadGlobalICByName);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue handler = profileTypeInfo->Get(slotId);
    if (handler.IsHeapObject()) {
        auto result = LoadGlobal(handler);
        if (!result.IsHole()) {
            return result;
        }
    }

    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    auto keyHandle = JSHandle<JSTaggedValue>(thread, key);
    auto receiverHandle = JSHandle<JSTaggedValue>(thread, globalValue);
    auto profileInfoHandle = JSHandle<JSTaggedValue>(thread, profileTypeInfo);
    LoadICRuntime icRuntime(thread, JSHandle<ProfileTypeInfo>::Cast(profileInfoHandle), slotId,
                            isTryLoad ? ICKind::TRY_NAMED_GLOBAL_LOAD_IC : ICKind::NAMED_GLOBAL_LOAD_IC);
    return icRuntime.LoadMiss(receiverHandle, keyHandle);
}

JSTaggedValue ICRuntimeStub::StoreGlobalICByName(JSThread *thread, JSFunction *func, JSTaggedValue globalValue,
                                                 JSTaggedValue key, JSTaggedValue value, uint32_t slotId,
                                                 bool isTryStore)
{
    INTERPRETER_TRACE(thread, StoreGlobalICByName);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue handler = profileTypeInfo->Get(slotId);
    if (handler.IsHeapObject()) {
        auto result = StoreGlobal(thread, value, handler);
        if (!result.IsHole()) {
            return result;
        }
    }

    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    auto keyHandle = JSHandle<JSTaggedValue>(thread, key);
    auto receiverHandle = JSHandle<JSTaggedValue>(thread, globalValue);
    auto valueHandle = JSHandle<JSTaggedValue>(thread, value);
    auto profileInfoHandle = JSHandle<JSTaggedValue>(thread, profileTypeInfo);
    StoreICRuntime icRuntime(thread, JSHandle<ProfileTypeInfo>::Cast(profileInfoHandle), slotId,
                             isTryStore ? ICKind::TRY_NAMED_GLOBAL_STORE_IC : ICKind::NAMED_GLOBAL_STORE_IC);
    return icRuntime.StoreMiss(receiverHandle, keyHandle, valueHandle);
}

JSTaggedValue ICRuntimeStub::CheckPolyHClass(JSTaggedValue cachedValue, JSHClass *hclass)
{
    if (!cachedValue.IsWeak()) {
        ASSERT(cachedValue.IsTaggedArray());
        TaggedArray *array = TaggedArray::Cast(cachedValue.GetHeapObject());
        uint32_t length = array->GetLength();
        for (uint32_t i = 0; i < length; i += 2) {  // 2 means one ic, two slot
            auto result = array->Get(i);
            if (result != JSTaggedValue::Undefined() && result.GetWeakReferent() == hclass) {
                return array->Get(i + 1);
            }
        }
    }
    return JSTaggedValue::Hole();
}

JSTaggedValue ICRuntimeStub::LoadICByName(JSThread *thread, JSFunction *func, JSTaggedValue receiver, JSTaggedValue key,
                                          uint32_t slotId)
{
    INTERPRETER_TRACE(thread, LoadICByName);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue hclassValue = profileTypeInfo->Get(slotId);
    if (hclassValue.IsHole()) {
        return JSTaggedValue::Hole();
    }
    if (LIKELY(receiver.IsHeapObject())) {
        auto hclass = receiver.GetTaggedObject()->GetClass();
        size_t index = slotId;
        do {
            if (!hclassValue.IsHeapObject()) {
                break;
            }
            if (LIKELY(hclassValue.GetTaggedObject() == hclass)) {
                JSTaggedValue handler = profileTypeInfo->Get(index + 1);
                if (UNLIKELY(handler.IsUndefined())) {
                    // handler is collected by GC.
                    // Clear profile data to fill it again in LoadMiss.
                    profileTypeInfo->Set(thread, index, JSTaggedValue::Undefined());
                    break;
                }
                return LoadICWithHandler(thread, receiver, receiver, handler);
            }
            index += 2U;
            hclassValue = profileTypeInfo->Get(index);
        } while (index < slotId + SlotSizeForICByName());
    }
    return LoadMissedICByName(thread, profileTypeInfo, receiver, key, slotId);
}

JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, JSFunction *func, JSTaggedValue receiver,
                                           JSTaggedValue key, uint32_t slotId)
{
    INTERPRETER_TRACE(thread, LoadICByValue);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue firstValue = profileTypeInfo->Get(slotId);
    if (firstValue.IsHole()) {
        return JSTaggedValue::Hole();
    }
    if (receiver.IsHeapObject() && firstValue.IsHeapObject()) {
        auto hclass = receiver.GetTaggedObject()->GetClass();
        // Check key
        if (firstValue == key) {
            JSTaggedValue hclassValue = profileTypeInfo->Get(slotId + 1);
            if (hclassValue.GetTaggedObject() == hclass) {
                JSTaggedValue handler = profileTypeInfo->Get(slotId + 2);
                if (UNLIKELY(handler.IsUndefined())) {
                    // handler is collected by GC.
                    // Clear profile data to fill it again in LoadMiss.
                    profileTypeInfo->Set(thread, slotId, JSTaggedValue::Undefined());
                    profileTypeInfo->Set(thread, slotId + 1, JSTaggedValue::Undefined());
                } else {
                    return LoadICWithHandler(thread, receiver, receiver, handler);
                }
            }
        }
        // Check element
        if (firstValue.GetTaggedObject() == hclass) {
            ASSERT(HandlerBase::IsElement(profileTypeInfo->Get(slotId + 1).GetInt()));
            if (!key.IsInt()) {
                JSTaggedValue handler = profileTypeInfo->Get(slotId + 1);
                auto handlerInfo = static_cast<uint32_t>(handler.GetInt());
                HandlerBase::SetKeyNotInt(handlerInfo);
                profileTypeInfo->Set(thread, slotId + 1, JSTaggedValue(handlerInfo));
            }
            return LoadElement(JSObject::Cast(receiver.GetHeapObject()), key);
        }
        size_t index = slotId + 2;
        while (index < SlotSizeForICByValue()) {
            firstValue = profileTypeInfo->Get(index);
            if (firstValue.GetRawData() == ToUintPtr(hclass)) {
                ASSERT(HandlerBase::IsElement(profileTypeInfo->Get(index + 1).GetInt()));
                if (!key.IsInt()) {
                    JSTaggedValue handler = profileTypeInfo->Get(index + 1);
                    auto handlerInfo = static_cast<uint32_t>(handler.GetInt());
                    HandlerBase::SetKeyNotInt(handlerInfo);
                    profileTypeInfo->Set(thread, index + 1, JSTaggedValue(handlerInfo));
                }
                return LoadElement(JSObject::Cast(receiver.GetHeapObject()), key);
            }
            index += 2;
        }
    }
    return LoadMissedICByValue(thread, profileTypeInfo, receiver, key, slotId);
}

JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, JSFunction *func, JSTaggedValue receiver,
                                            JSTaggedValue key, JSTaggedValue value, uint32_t slotId)
{
    INTERPRETER_TRACE(thread, StoreICByValue);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue firstValue = profileTypeInfo->Get(slotId);
    if (firstValue.IsHole()) {
        return JSTaggedValue::Hole();
    }
    if (receiver.IsHeapObject() && firstValue.IsHeapObject()) {
        auto hclass = receiver.GetTaggedObject()->GetClass();
        // Check key
        if (firstValue == key) {
            JSTaggedValue hclassValue = profileTypeInfo->Get(slotId + 1);
            if (hclassValue.GetTaggedObject() == hclass) {
                JSTaggedValue handler = profileTypeInfo->Get(slotId + 2);
                if (UNLIKELY(handler.IsUndefined())) {
                    // handler is collected by GC.
                    // Clear profile data to fill it again in StoreMiss.
                    profileTypeInfo->Set(thread, slotId, JSTaggedValue::Undefined());
                    profileTypeInfo->Set(thread, slotId + 1, JSTaggedValue::Undefined());
                } else {
                    return StoreICWithHandler(thread, receiver, receiver, value, handler);
                }
            }
        }
        // Check element
        if (firstValue.GetTaggedObject() == hclass) {
            return StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, profileTypeInfo,
                                slotId + 1);
        }
        size_t index = slotId + 2;
        while (index < SlotSizeForICByValue()) {
            firstValue = profileTypeInfo->Get(index);
            if (firstValue.GetRawData() == ToUintPtr(hclass)) {
                return StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, profileTypeInfo,
                                    index + 1);
            }
            index += 2;
        }
    }

    return StoreMissedICByValue(thread, profileTypeInfo, receiver, key, value, slotId);
}

JSTaggedValue ICRuntimeStub::StoreICByName(JSThread *thread, JSFunction *func, JSTaggedValue receiver,
                                           JSTaggedValue key, JSTaggedValue value, uint32_t slotId)
{
    INTERPRETER_TRACE(thread, StoreICByName);
    ASSERT(HaveICForFunction(func));
    slotId = MapSlotId(func, slotId);
    ProfileTypeInfo *profileTypeInfo = GetRuntimeProfileTypeInfo(func);
    JSTaggedValue hclassValue = profileTypeInfo->Get(slotId);
    if (hclassValue.IsHole()) {
        return JSTaggedValue::Hole();
    }
    if (receiver.IsHeapObject()) {
        auto hclass = receiver.GetTaggedObject()->GetClass();
        size_t index = slotId;
        do {
            if (!hclassValue.IsHeapObject()) {
                break;
            }
            if (LIKELY(hclassValue.GetTaggedObject() == hclass)) {
                JSTaggedValue handler = profileTypeInfo->Get(index + 1);
                if (UNLIKELY(handler.IsUndefined())) {
                    // handler is collected by GC.
                    // Clear profile data to fill it again in StoreMiss.
                    profileTypeInfo->Set(thread, index, JSTaggedValue::Undefined());
                    break;
                }
                return StoreICWithHandler(thread, receiver, receiver, value, handler);
            }
            index += 2U;
            hclassValue = profileTypeInfo->Get(index);
        } while (index < slotId + SlotSizeForICByName());
    }
    return StoreMissedICByName(thread, profileTypeInfo, receiver, key, value, slotId);
}

JSTaggedValue ICRuntimeStub::StoreICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
                                                JSTaggedValue value, JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, StoreICWithHandler);
    if (handler.IsInt()) {
        auto handlerInfo = static_cast<uint32_t>(handler.GetInt());
        if (HandlerBase::IsField(handlerInfo)) {
            StoreField(thread, JSObject::Cast(receiver.GetHeapObject()), value, handlerInfo);
            return JSTaggedValue::Undefined();
        }
        ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo));
        auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo);
        return FastRuntimeStub::CallSetter(thread, JSTaggedValue(receiver), value, accessor);
    }
    if (handler.IsTransitionHandler()) {
        StoreWithTransition(thread, JSObject::Cast(receiver.GetHeapObject()), value, handler);
        return JSTaggedValue::Undefined();
    }
    if (handler.IsPrototypeHandler()) {
        return StorePrototype(thread, receiver, value, handler);
    }
    if (handler.IsPropertyBox()) {
        return StoreGlobal(thread, value, handler);
    }
    return JSTaggedValue::Undefined();
}

JSTaggedValue ICRuntimeStub::StorePrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue value,
                                            JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, StorePrototype);
    ASSERT(handler.IsPrototypeHandler());
    PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject());
    auto cellValue = prototypeHandler->GetProtoCell();
    ASSERT(cellValue.IsProtoChangeMarker());
    ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject());
    if (cell->GetHasChanged()) {
        return JSTaggedValue::Hole();
    }
    auto holder = prototypeHandler->GetHolder();
    JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo();
    return StoreICWithHandler(thread, receiver, holder, value, handlerInfo);
}

void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JSTaggedValue value,
                                        JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, StoreWithTransition);
    TransitionHandler *transitionHandler = TransitionHandler::Cast(handler.GetTaggedObject());
    JSHClass *newHClass = JSHClass::Cast(transitionHandler->GetTransitionHClass().GetTaggedObject());
    receiver->SetClass(newHClass);
    uint32_t handlerInfo = transitionHandler->GetHandlerInfo().GetInt();
    ASSERT(HandlerBase::IsField(handlerInfo));

    if (!HandlerBase::IsInlinedProps(handlerInfo)) {
        TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject());
        int capacity = array->GetLength();
        int index = HandlerBase::GetOffset(handlerInfo);
        if (index >= capacity) {
            [[maybe_unused]] EcmaHandleScope handleScope(thread);
            transitionHandler->SetArrayOverflowed(thread);
            ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
            JSHandle<TaggedArray> properties;
            JSHandle<JSObject> objHandle(thread, receiver);
            JSHandle<JSTaggedValue> valueHandle(thread, value);
            if (capacity == 0) {
                capacity = JSObject::MIN_PROPERTIES_LENGTH;
                properties = factory->NewTaggedArray(capacity);
            } else {
                auto arrayHandle = JSHandle<TaggedArray>(thread, array);
                properties = factory->CopyArray(arrayHandle, capacity, JSObject::ComputePropertyCapacity(capacity));
            }
            properties->Set(thread, index, valueHandle);
            objHandle->SetProperties(thread, properties);
            return;
        }
        array->Set(thread, index, value);
        return;
    }
    StoreField(thread, receiver, value, handlerInfo);
}

void ICRuntimeStub::StoreField(JSThread *thread, JSObject *receiver, JSTaggedValue value, uint32_t handler)
{
    INTERPRETER_TRACE(thread, StoreField);
    int index = HandlerBase::GetOffset(handler);
    if (HandlerBase::IsInlinedProps(handler)) {
        SET_VALUE_WITH_BARRIER(thread, receiver, index * JSTaggedValue::TaggedTypeSize(), value);
        return;
    }
    TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetHeapObject());
    ASSERT(index < static_cast<int>(array->GetLength()));
    array->Set(thread, index, value);
}

JSTaggedValue ICRuntimeStub::LoadFromField(JSObject *receiver, uint32_t handlerInfo)
{
    int index = HandlerBase::GetOffset(handlerInfo);
    if (HandlerBase::IsInlinedProps(handlerInfo)) {
        return JSTaggedValue(GET_VALUE(receiver, index * JSTaggedValue::TaggedTypeSize()));
    }
    return TaggedArray::Cast(receiver->GetProperties().GetHeapObject())->Get(index);
}

JSTaggedValue ICRuntimeStub::LoadGlobal(JSTaggedValue handler)
{
    ASSERT(handler.IsPropertyBox());
    PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject());
    if (cell->IsInvalid()) {
        return JSTaggedValue::Hole();
    }
    JSTaggedValue ret = cell->GetValue();
    ASSERT(!ret.IsAccessorData());
    return ret;
}

JSTaggedValue ICRuntimeStub::StoreGlobal(JSThread *thread, JSTaggedValue value, JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, StoreGlobal);
    ASSERT(handler.IsPropertyBox());
    PropertyBox *cell = PropertyBox::Cast(handler.GetHeapObject());
    if (cell->IsInvalid()) {
        return JSTaggedValue::Hole();
    }
    ASSERT(!cell->GetValue().IsAccessorData());
    cell->SetValue(thread, value);
    return JSTaggedValue::Undefined();
}

JSTaggedValue ICRuntimeStub::LoadPrototype(JSThread *thread, JSTaggedValue receiver, JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, LoadPrototype);
    ASSERT(handler.IsPrototypeHandler());
    PrototypeHandler *prototypeHandler = PrototypeHandler::Cast(handler.GetTaggedObject());
    auto cellValue = prototypeHandler->GetProtoCell();
    if (cellValue.IsFalse()) {
        // property was not found and object has no prototype
        return JSTaggedValue::Hole();
    }
    ASSERT(cellValue.IsProtoChangeMarker());
    ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cellValue.GetHeapObject());
    if (cell->GetHasChanged()) {
        return JSTaggedValue::Hole();
    }
    auto holder = prototypeHandler->GetHolder();
    JSTaggedValue handlerInfo = prototypeHandler->GetHandlerInfo();
    return LoadICWithHandler(thread, receiver, holder, handlerInfo);
}

JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder,
                                               JSTaggedValue handler)
{
    INTERPRETER_TRACE(thread, LoadICWithHandler);
    if (LIKELY(handler.IsInt())) {
        auto handlerInfo = static_cast<uint32_t>(handler.GetInt());
        if (LIKELY(HandlerBase::IsField(handlerInfo))) {
            return LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo);
        }
        if (HandlerBase::IsNonExist(handlerInfo)) {
            return JSTaggedValue::Hole();
        }
        ASSERT(HandlerBase::IsAccessor(handlerInfo) || HandlerBase::IsInternalAccessor(handlerInfo));
        auto accessor = LoadFromField(JSObject::Cast(holder.GetHeapObject()), handlerInfo);
        return FastRuntimeStub::CallGetter(thread, receiver, holder, accessor);
    }

    if (handler.IsPrototypeHandler()) {
        return LoadPrototype(thread, receiver, handler);
    }

    return LoadGlobal(handler);
}

JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTaggedValue key)
{
    auto index = TryToElementsIndex(key);
    if (index >= JSObject::MAX_ELEMENT_INDEX) {
        return JSTaggedValue::Hole();
    }
    uint32_t elementIndex = index;
    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject());
    if (elements->GetLength() <= elementIndex) {
        return JSTaggedValue::Hole();
    }

    JSTaggedValue value = elements->Get(elementIndex);
    // TaggedArray elements
    return value;
}

JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, JSTaggedValue value,
                                          ProfileTypeInfo *profileTypeInfo, uint32_t slotId)
{
    INTERPRETER_TRACE(thread, StoreElement);
    JSTaggedValue handler = profileTypeInfo->Get(slotId);
    auto handlerInfo = static_cast<uint32_t>(handler.GetInt());
    ASSERT(HandlerBase::IsElement(handlerInfo));
    if (!key.IsInt()) {
        HandlerBase::SetKeyNotInt(handlerInfo);
        profileTypeInfo->Set(thread, slotId, JSTaggedValue(handlerInfo));
    }
    auto index = TryToElementsIndex(key);
    if (index >= JSObject::MAX_ELEMENT_INDEX) {
        return JSTaggedValue::Hole();
    }
    uint32_t elementIndex = index;
    if (HandlerBase::IsJSArray(handlerInfo)) {
        JSArray *arr = JSArray::Cast(receiver);
        uint32_t oldLength = arr->GetArrayLength();
        if (elementIndex >= oldLength) {
            arr->SetArrayLength(thread, elementIndex + 1);
        }
    }
    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject());
    uint32_t capacity = elements->GetLength();
    if (elementIndex >= capacity) {
        HandlerBase::SetArrayOverflowed(handlerInfo);
        profileTypeInfo->Set(thread, slotId, JSTaggedValue(handlerInfo));
        if (JSObject::ShouldTransToDict(capacity, elementIndex)) {
            return JSTaggedValue::Hole();
        }
        [[maybe_unused]] EcmaHandleScope handleScope(thread);
        JSHandle<JSObject> receiverHandle(thread, receiver);
        JSHandle<JSTaggedValue> valueHandle(thread, value);
        elements =
            *JSObject::GrowElementsCapacity(thread, receiverHandle, JSObject::ComputeElementCapacity(elementIndex + 1));
        receiverHandle->SetElements(thread, JSTaggedValue(elements));
        elements->Set(thread, elementIndex, valueHandle);
        return JSTaggedValue::Undefined();
    }
    elements->Set(thread, elementIndex, value);
    return JSTaggedValue::Undefined();
}

uint32_t ICRuntimeStub::TryToElementsIndex(JSTaggedValue key)
{
    if (LIKELY(key.IsInt())) {
        return key.GetInt();
    }

    if (key.IsString()) {
        uint32_t index = 0;
        if (JSTaggedValue::StringToElementIndex(key, &index)) {
            return index;
        }
    }

    if (key.IsDouble()) {
        double number = key.GetDouble();
        auto integer = static_cast<uint32_t>(number);
        if (number == integer) {
            return integer;
        }
    }

    return JSObject::MAX_ELEMENT_INDEX;
}
}  // namespace ark::ecmascript

#endif  // ECMASCRIPT_IC_IC_RUNTIME_STUB_INL_H_
